<!--
 * @author: Soveilove
 * @description: 在线聊天室
 * @remarks: 这玩意是可以封装成一个hooks,然后到你用的时候直接套上就完事的,
            属于很牛逼很高深的东西,因此今天的重点就是这个玩意
-->

<template>
  <section class="chat-room">
    <a-card class="card-chat" hoverable>
      <template #title>
        <span>一个基于socket.io的在线交流功能，正在升级中...</span>
      </template>
      <ul class="msg-box" ref="msgBoxRef">
        <li
          v-for="(msg, index) in msgList"
          :key="index"
          :class="msg.customClass"
        >
          <!-- 谁进来谁出去的全局广播 -->
          <div v-if="msg.type == 'sys_msg'">
            <span class="content">{{ msg.content }}</span>
          </div>
          <!-- 自己的逼话,放右边 -->
          <div v-if="msg.type == 'mine'" class="chat-item-wrap">
            <img src="@/assets/img/chat-avatar.png" />
            <div class="txt-wrap">
              <span>{{ msg.time }}</span
              >{{ msg.user }}
              <div v-if="msg.pic" class="chat-text clearfix">
                <el-image
                  style="height: 80px"
                  :src="`http://image.dbbqb.com/${msg.content}`"
                  fit="fill"
                />
              </div>
              <div v-else class="chat-text clearfix">
                <span>{{ msg.content }}</span>
              </div>
            </div>
          </div>
          <!-- 他的逼话,放左边 -->
          <div v-if="msg.type == 'others'" class="chat-item-wrap">
            <img src="@/assets/img/chat-avatar.png" />
            <div class="txt-wrap">
              <span>{{ msg.user }}</span
              >{{ msg.time }}
              <div v-if="msg.pic" class="chat-text clearfix">
                <el-image
                  style="height: 80px"
                  :src="`http://image.dbbqb.com/${msg.content}`"
                  fit="fill"
                />
              </div>
              <div v-else class="chat-text clearfix">
                <span>{{ msg.content }}</span>
              </div>
            </div>
          </div>
        </li>
      </ul>

      <div class="chat-tool">
        <chat-tool-bar :chatForm="chatForm" />
        <a-form class="form-chat" :model="chatForm" :rules="chatRules">
          <a-form-item
            name="chatContent"
            class="form-item--content"
            :wrapper-col="{ span: 24 }"
          >
            <a-textarea
              v-model:value="chatForm.chatContent"
              :autoSize="{ minRows: 2, maxRows: 2 }"
              placeholder="输入内容后点击发送..."
              allowClear
              @keydown="onKeydownChat"
            />
          </a-form-item>
          <a-form-item :wrapper-col="{ span: 14 }">
            <a-dropdown-button type="primary" v-on:click="sendChatContent">
              发送
              <template #overlay>
                <a-menu>
                  <a-radio-group v-model:value="triggerType">
                    <a-menu-item key="1">
                      <a-radio :value="1">按Enter键发送消息</a-radio>
                    </a-menu-item>
                    <a-menu-item key="2">
                      <a-radio :value="2">按Ctrl+Enter键发送消息</a-radio>
                    </a-menu-item>
                  </a-radio-group>
                </a-menu>
              </template>
              <template #icon><DownOutlined /></template>
            </a-dropdown-button>
          </a-form-item>
        </a-form>
      </div>
    </a-card>
  </section>
</template>

<script lang="ts">
import {
  defineComponent,
  nextTick,
  onMounted,
  reactive,
  ref,
  toRaw,
  watch,
} from 'vue';
import { DownOutlined } from '@ant-design/icons-vue';
import { Form, Input, Dropdown, Menu, Radio } from 'ant-design-vue';
import { useWebSocket } from '@/hooks';
import { format } from '@/utils/date-utils';
import { MsgListDTO } from '@/bean/dto';
import ChatToolBar from './chat-tool-bar/index.vue';
import { provide } from 'vue';

export default defineComponent({
  components: {
    DownOutlined,
    ChatToolBar,
    [Form.name]: Form,
    [Form.Item.name]: Form.Item,
    [Input.TextArea.name]: Input.TextArea,
    [Dropdown.Button.name]: Dropdown.Button,
    [Menu.name]: Menu,
    [Menu.Item.name]: Menu.Item,
    [Radio.name]: Radio,
    [Radio.Group.name]: Radio.Group,
  },
  setup() {
    // form表单的hooks函数
    const useForm = Form.useForm;
    // 1代表Enter触发，2代表Ctrl+Enter触发
    const triggerType = ref<number>(1);
    const msgBoxRef = ref<HTMLElement>();
    const chatForm = reactive({ chatContent: '' });
    const socket = useWebSocket();
    // 因为我是直接push进去的,所以他根本没有监听到变化
    const msgList = reactive<MsgListDTO[]>([]);
    const chatRules = reactive({
      chatContent: [
        { required: true, message: '请输入聊天内容', trigger: ['change'] },
      ],
    });
    const { resetFields, validate } = useForm(chatForm, chatRules);

    // 一开始我想直接偷懒使用watchEffect,但是我想到自动收集依赖,
    // 还是没有自己指定收集依赖来的香吧,但是我打算两个都跑一下,跑过了 上面那玩意就监听不了
    watch(
      /* 依赖源收集函数 */
      msgList,
      () => {
        /* 依赖源改变时的回调函数 */

        /* 这里只能使用nextTick,nextTick就是用来告知DOM什么时候更新完，当DOM更新完毕后，nextTick方法里面的回调就会执行。
        因为你渲染进去后才会造成scrollTop的变化,你才能把他调下去
         */
        nextTick(() => {
          const el = msgBoxRef.value as HTMLElement;
          const scrollTop = el.scrollHeight - el.clientHeight;

          /* 突然有个bug,如果主播的鼠标进去了,不应该让他再次回到底部,
          有个思路,应该用鼠标滚轮绑定下,如果使用鼠标滚轮,
          则不弄这个,如果滚动条在底部某个区间再解绑 */
          if (scrollTop) {
            el.scrollTop = scrollTop;
          }
        });
      }
    );

    // 这里我思考一下,我认为用户这玩意可以改一改,使用你登进我这个网站使用的名称怎么样,
    // 这些小细节关系不大,有思路 就是在store那里获取到CommentuseInfo,如果没有填写就按连进来的socketid来当用户名
    const sendChatContent = () => {
      validate()
        .then(() => {
          socket.emit('chat', toRaw(chatForm.chatContent));
          // 这里再加个类型判断就好了,就是说我只要加个是否是图片类型就好
          msgList.push({
            time: format(new Date(), 'HH:mm:ss'),
            user: '我说',
            content: toRaw(chatForm.chatContent),
            type: 'mine',
            customClass: 'mine',
            pic: false,
          });
          resetFields();
        })
        .catch((err) => {
          console.log('error', err);
        });
    };

    provide('sendChatImg', sendChatImg);
    function sendChatImg(url: string) {
      socket.emit('chatImg', url);
      msgList.push({
        time: format(new Date(), 'HH:mm:ss'),
        user: '我说',
        content: url,
        type: 'mine',
        customClass: 'mine',
        pic: true,
      });
    }

    const onKeydownChat = (e: KeyboardEvent) => {
      if (
        (triggerType.value === 1 && e.key === 'Enter') ||
        (triggerType.value === 2 && e.ctrlKey && e.key === 'Enter')
      ) {
        e.preventDefault();
        sendChatContent();
      }
    };

    onMounted(() => {
      // 这个是socket 特殊逻辑的监听函数,因此我没打算抽离到useWebsocekt里面
      socket.on('singleMsg', function (msg) {
        switch (msg.event) {
          case 'greet_from_server':
            msgList.push({
              time: format(new Date(), 'HH:mm:ss'),
              user: '系统通知',
              content: msg.data.content,
              type: 'sys_msg',
              customClass: 'sys_msg',
              pic: false,
            });
            break;
        }
        // console.log(msgList);
      });

      socket.on('broadcastAll', function (msg) {
        console.log(msg);
      });

      socket.on('broadcast', function (msg) {
        console.log(msg);
        switch (msg.event) {
          case 'new_user_join':
            msgList.push({
              time: format(new Date(), 'HH:mm:ss'),
              user: '系统通知',
              content: `用户 ${msg.data.user} 加入了聊天室......`,
              type: 'sys_msg',
              customClass: 'sys_msg',
              pic: false,
            });
            break;
          case 'someone_exit':
            msgList.push({
              time: format(new Date(), 'HH:mm:ss'),
              user: '系统通知',
              content: `用户 ${msg.data.user} 退出了聊天室......`,
              type: 'sys_msg',
              customClass: 'sys_msg',
              pic: false,
            });
            break;
          case 'new_chat_content':
            msgList.push({
              time: format(new Date(), 'HH:mm:ss'),
              user: msg.data.user,
              content: msg.data.content,
              type: 'others',
              customClass: 'others',
              pic: false,
            });
            break;
          case 'new_chat_contentImg':
            msgList.push({
              time: format(new Date(), 'HH:mm:ss'),
              user: msg.data.user,
              content: msg.data.content,
              type: 'others',
              customClass: 'others',
              pic: true,
            });
            break;
        }
      });
    });

    return {
      msgBoxRef,
      triggerType,
      chatForm,
      chatRules,
      onKeydownChat,
      sendChatContent,
      msgList,
    };
  },
});
</script>

<style scoped lang="scss" src="./index.scss"></style>
